local skynet = require "skynet"
require "skynet.manager"
local SkynetQueue = require "skynet.queue"
local ServiceName = require "enum.ServiceName"
local Eventer = require "core.Eventer"
local PlayerEvent = require "modules.player.PlayerEvent"

local gates = {}

---@alias PlayerId string
---@alias SkynetQueue function
---@type table<PlayerId, SkynetQueue>
local playerQueues = {}

--- playerId -> {fd= , agent=, gate=, protocol=, addr=}
local players = {}
local fd2player = {}

local login, logout

---@class PlayerMgr
local M = {}
function M.send(playerId, protoId, data)
    local player = players[playerId]
    if not player then
        -- 玩家没有在线
        return
    end

    skynet.send(player.gate, "send", player.fd, protoId, data)
end

-- 收到广播消息，根据网关发送
function M.boardcast(playerIds, protoId, data)
    local fdsGroup = {}
    for _, playerId in ipairs(playerIds) do
        local player = players[playerId]
        if player then
            local fds = fdsGroup[player.gate] or {}
            fdsGroup[player.gate] = fds
            table.insert(fds, player.fd)
        end
    end

    for gate, gatefds in pairs(fdsGroup) do
        skynet.send(gate, "lua", "broadcast", gatefds, protoId, data)
    end
end

function M.sendAll(protoId, data)
    for _, gate in pairs(gates) do
        gate.sendAll(protoId, data)
    end
end

local function callQueue(func, playerId, ...)
    local playerQueue = playerQueues[playerId]
    if not playerQueue then
        playerQueue = SkynetQueue()
        playerQueues[playerId] = playerQueue
    end

    return playerQueue(func, playerId, ...)
end

local function login(playerId, fd, gate)
    if players[playerId] then
        return false
    end

    players[playerId] = {
        id = playerId,
        fd = fd,
        gate = gates[gate]
    }

    GLOGF("player login, id: %s, fd: %d", playerId, fd)
    Eventer.dispatchSync(PlayerEvent.PLAYER_LOGIN, playerId)
    return true
end

function M.login(playerId, fd, gate)
    return callQueue(login, playerId, fd, gate)
end

local function logout(playerId)
    local player = players[playerId]
    if not player then
        GWARN("logout unknown player, playerId: " .. playerId)
        return
    end

    skynet.send(player.gate, "lua", "close", player.fd)
    fd2player[player.fd] = nil
    players[playerId] = nil
    GLOG("player logout, id: " .. playerId)
    Eventer.dispatchSync(PlayerEvent.PLAYER_LOGOUT, playerId)
end

function M.logout(playerId)
    local player = players[playerId]
    if not player then
        return
    end

    callQueue(logout, playerId)
    playerQueues[playerId] = nil
end

function M.start(gateAddress)
    for i = 1, #gateAddress do
        local addr = gateAddress[i]
        gates[addr] = addr
    end
end

function M.init()
    skynet.dispatch_lua(M)
    skynet.register(ServiceName.PLAYER_MGR)
end

return M
